function mxLayoutManager(graph)
{
	var self = this;

	this.undoHandler = function(sender, evt)
	{
		if (self.isEnabled())
		{
			self.beforeUndo(evt.getArgAt(0));
		}
	};
	this.moveHandler = function(sender, evt)
	{
		if (self.isEnabled())
		{
			self.cellsMoved(evt.getArgAt(0), evt.getArgAt(5));
		}
	};
	this.setGraph(graph);
};
mxLayoutManager.prototype = new mxEventSource();
mxLayoutManager.prototype.constructor = mxLayoutManager;
mxLayoutManager.prototype.graph = null;
mxLayoutManager.prototype.bubbling = true;
mxLayoutManager.prototype.enabled = true;
mxLayoutManager.prototype.updateHandler = null;
mxLayoutManager.prototype.moveHandler = null;
mxLayoutManager.prototype.isEnabled = function()
{
	return this.enabled;
};
mxLayoutManager.prototype.setEnabled = function(enabled)
{
	this.enabled = enabled;
};
mxLayoutManager.prototype.isBubbling = function()
{
	return this.bubbling;
};
mxLayoutManager.prototype.setBubbling = function(value)
{
	this.bubbling = value;
};
mxLayoutManager.prototype.getGraph = function()
{
	return this.graph;
};
mxLayoutManager.prototype.setGraph = function(graph)
{
	if (this.graph != null)
	{
		var model = this.graph.getModel();
		model.removeListener(this.undoHandler);
		this.graph.removeListener(this.moveHandler);
	}
	this.graph = graph;

	if (this.graph != null)
	{
		var model = this.graph.getModel();
		model.addListener(mxEvent.BEFORE_UNDO, this.undoHandler);
		this.graph.addListener(mxEvent.MOVE_CELLS, this.moveHandler);
	}
};
mxLayoutManager.prototype.getLayout = function(parent)
{
	return null;
};
mxLayoutManager.prototype.beforeUndo = function(undoableEdit)
{
	var cells = this.getCellsForChanges(undoableEdit.changes);
	var model = this.getGraph().getModel();

	if (this.isBubbling())
	{
		var tmp = model.getParents(cells);

		while (tmp.length > 0)
		{
			cells = cells.concat(tmp);
			tmp = model.getParents(tmp);
		}
	}
	this.layoutCells(mxUtils.sortCells(cells, false));
};
mxLayoutManager.prototype.cellsMoved = function(cells, evt)
{
	if (cells != null && evt != null)
	{
		var point = mxUtils.convertPoint(this.getGraph().container, evt.clientX, evt.clientY);
		var model = this.getGraph().getModel();

		for (var i = 0; i < cells.length; i++)
		{
			var layout = this.getLayout(model.getParent(cells[i]));

			if (layout != null)
			{
				layout.moveCell(cells[i], point.x, point.y);
			}
		}
	}
};
mxLayoutManager.prototype.getCellsForChanges = function(changes)
{
	var result = [];
	var hash = {};

	for (var i = 0; i < changes.length; i++)
	{
		var change = changes[i];

		if (change instanceof mxRootChange)
		{
			return [];
		}
		else
		{
			var cells = this.getCellsForChange(change);

			for (var j = 0; j < cells.length; j++)
			{
				if (cells[j] != null)
				{
					var id = mxCellPath.create(cells[j]);

					if (hash[id] == null)
					{
						hash[id] = cells[j];
						result.push(cells[j]);
					}
				}
			}
		}
	}
	return result;
};
mxLayoutManager.prototype.getCellsForChange = function(change)
{
	var model = this.getGraph().getModel();

	if (change.constructor == mxChildChange)
	{
		return [change.child, change.previous, model.getParent(change.child)];
	}
	else if (change.constructor == mxTerminalChange || change.constructor == mxGeometryChange)
	{
		return [change.cell, model.getParent(change.cell)];
	}
	return [];
};
mxLayoutManager.prototype.layoutCells = function(cells)
{
	if (cells.length > 0)
	{
		var model = this.getGraph().getModel();
		model.beginUpdate();

		try
		{
			var last = null;

			for (var i = 0; i < cells.length; i++)
			{
				if (cells[i] != model.getRoot() && cells[i] != last)
				{
					last = cells[i];
					this.executeLayout(this.getLayout(last), last);
				}
			}
			this.fireEvent(mxEvent.LAYOUT_CELLS, new mxEventObject([cells]));
		}
		finally
		{
			model.endUpdate();
		}
	}
};
mxLayoutManager.prototype.executeLayout = function(layout, parent)
{
	if (layout != null && parent != null)
	{
		layout.execute(parent);
	}
};
mxLayoutManager.prototype.destroy = function()
{
	this.setGraph(null);
};